'''
Created on Jul 21, 2011

@author: Krystian Brachmanski
'''

import logging
import subprocess
import os

class BaseTexReportGenerator:
    '''
    Base class responsible for generating the latex sections of the document

    @author: Krystian Brachmanski
    @requires: logging
    '''
    log = logging.getLogger('yapcg.perfparser.latexUtils')
    '''
    Logger
    '''

    def generateGeneralSectionFigure(self, path, property=""):
        '''
    	This method shall be used to generate the figure section
    	@param path: the path to the figure (picture)
    	@param property: additional property to be used for formatting the figure
    	@return: the string containing the latex begin/end section with the formatted figure
    	'''
        return "\\begin{figure}[h!b!p!]\n\
\\begin{center}\n\
\\includegraphics[" + property + "width=0.85\\textwidth]{" + path + "}\n\
\\end{center}\n\
\\end{figure}\n"

class GeneralTexReportGenerator(BaseTexReportGenerator):
    
    def __init__(self, title=None):
        self.generalSectionFigures = []
        self.title = title
        self.text = None
        self.additionalTable = None
    
    def addFigure(self,path):
        self.generalSectionFigures.append(path)
    
    @property
    def title(self,title):
        self.title = title
    
    @property
    def text(self,text):
        self.text = text
    
    @property
    def additionalTables(self,additionalTables):
        self.additionalTable = additionalTables
    
    def generateSection(self,property=""):
        #tmp = "\\section{" + self.title + "}\n\n"
        tmp = "\\subsection{" + self.title + "}\n"
        if (self.text != None):
            tmp += self.text

        for fig in self.generalSectionFigures:
            tmp += self.generateGeneralSectionFigure(fig,property)		

        if (self.additionalTable != None):
            tmp += self.additionalTable

        tmp += "\\clearpage\n"
        tmp += "\\pagebreak\n"
        return tmp        
    

class TexReportGenerator(BaseTexReportGenerator):
    '''
    Class responsible for generating the report .tex files that shall be included in the main report
    
    @author: Krystian Brachmanski
    @requires: 
    '''

    def __init__(self):
        '''
        Constructor - initializes the structured to the default values
        '''
        self.generalSectionFigures = []
        
        self.cpuGeneralSectionGenerator = GeneralTexReportGenerator()
        self.generalCpuSectionFigures = []        
        self.generalMemSectionFigures = []
        
        self.processCpuDetailFigures = []
        self.processMemDetailFigures = []
        self.processIoDetailFigures = [] 
        self.bootchartFigures = []               
        
        self.generalCpuStatistics = None
        self.ignoredExecutionStats = None
        self.ignoredMaxCpuStats = None
        self.ignoredMaxMemStats = None

    def addGeneralSectionFigure(self, path):
        '''
        Add a figure that will be placed in the general process section of the document
        '''
        self.generalSectionFigures.append(path)
        return

    def addGeneralCpuSectionFigure(self, path):
        '''
        Add a figure that will be placed in the system overview section of the document
        '''        
        self.generalCpuSectionFigures.append(path)
        self.cpuGeneralSectionGenerator.addFigure(path)
        return
    
    def addBootchartSectionFigure(self, path):
        self.bootchartFigures.append(path)
        return
    
    def addGeneralMemSectionFigure(self, path):
        self.generalMemSectionFigures.append(path)
        return
       
    def addDetailCpuSectionFigure(self, path, name):
        self.processCpuDetailFigures.append((name,path))
        return

    def addDetailMemSectionFigure(self, path, name):
        self.processMemDetailFigures.append((name,path))
        return
                
    def generateSubSectionProcess(self,name,value):
        tmp = ""
        tmp += "\\subsection {" + name +"}\n"
        tmp += self.generateGeneralSectionFigure(value,"height=280pt,")
           
        # If memory charts do not match the print warning and proceed to the next cpu chart
        try:
            i = [x[0] for x in self.processMemDetailFigures].index(name)
            mem = self.processMemDetailFigures[i]
            tmp += self.generateGeneralSectionFigure(mem[1])

        except ValueError:
            self.log.warning("No matching memory chart found for " + name)
            
        # If io charts do not match the print warning and proceed to the next cpu chart
        try:
            i = [x[0] for x in self.processIoDetailFigures].index(name)
            io = self.processIoDetailFigures[i]
            tmp += self.generateGeneralSectionFigure(io[1])

        except ValueError:
            self.log.warning("No matching memory chart found for " + name)
            
        tmp += "\\clearpage\n"
        tmp += "\\pagebreak\n"
        
        return tmp
        
    def generateProcessDetailSection(self):
        tmp = "\\section{Process details}\n\n"
                
        for fig in self.processCpuDetailFigures:
            tmp += self.generateSubSectionProcess(fig[0],fig[1])
                
        return tmp

    def generateIgnoredSection(self):
        tmp = "\\subsection{Supressed processes}\n"
        tmp += "The table presented below shows the statistics of the processes that were added to the ignored list (these processes will not be displayed on any chart).\n"
        tmp += "\\begin{table}[ht]\n \
\\caption{Supressed (ignored) processes table}\n \
\\centering\n \
\\begin{tabular}{c c c c} \n \
\\hline \n \
\\hline \n \
\\textbf{Process Group} & \\textbf{No. processes created} & \\textbf{max. CPU [\%]} & \\textbf{max. mem. [\%]} \\\\ [0.5ex] \n \
\\hline \n"

        for i in self.ignoredExecutionStats.keys():
            tmp += "\\textbf{" + i + "} & " + str(self.ignoredExecutionStats[i]) + " & ";
            if self.ignoredMaxCpuStats != None:
                tmp += str(self.ignoredMaxCpuStats[i])
            else:
                tmp += " - "
            tmp += " & "
            if self.ignoredMaxMemStats != None:
                tmp += str(self.ignoredMaxMemStats[i])
            else:
                tmp += " - "
            tmp += "\\\\ [1ex]\n"
        tmp += "\
\\hline \n \
\\end{tabular} \n \
\\label{table:nonlin} \n \
\\end{table}\n\n"
        
        tmp += "\\subsubsection{Execution statistics}\n"
        tmp+="Statistics for ignored processes:\n"
        tmp += "\\begin{enumerate}\n"
            
        for i in self.ignoredExecutionStats.keys():
            tmp +="\\item \\textbf{" + i + "} : number of processes created = \\textbf{" + str(self.ignoredExecutionStats[i]) + "} \n"
        tmp += "\\end{enumerate}\n"
        
        if self.ignoredMaxCpuStats != None:
            tmp += "\\subsubsection{Max. CPU usage statistics}\n"
            tmp += "Statistics for ignored processes:\n"
            tmp += "\\begin{enumerate}\n"
            
            for i in self.ignoredMaxCpuStats.keys():
                tmp+="\\item \\textbf{" + i + "} : max. CPU usage = \\textbf{" + str(self.ignoredMaxCpuStats[i]) + "} [\%]\n"                        
            tmp += "\\end{enumerate}\n"

        if self.ignoredMaxMemStats != None:
            tmp += "\\subsubsection{Max. memory usage statistics}\n"
            tmp+="Statistics for ignored processes:\n"
            tmp += "\\begin{enumerate}\n"

            for i in self.ignoredMaxMemStats.keys():
                tmp+="\\item \\textbf{" + i + "} : max. mem usage = \\textbf{" + str(self.ignoredMaxMemStats[i]) + "} [\%] \n"                        
            tmp += "\\end{enumerate}\n"

        tmp += "\\pagebreak\n\n"
        return tmp
        
    def generateCpuStatisticsTable(self):
        if self.generalCpuStatistics != None:
            tmp = "\\begin{table}[ht]\n \
\\caption{Detailed CPU statistics}\n \
\\centering\n \
\\begin{tabular}{c c c c c} \n \
\\hline \n \
\\hline \n \
\\textbf{Type} & \\textbf{Min} & \\textbf{Avg} & \\textbf{Q95} & \\textbf{Max} \\\\ [0.5ex] \n \
\\hline \n"
            # total shall go to the end
            for i in sorted(self.generalCpuStatistics.keys()):
                if i != "total":
                    stats = self.generalCpuStatistics[i]
                    tmp += "\\textbf{" + i + "} & " + str("%.2f" % stats.min) + " & " + str("%.2f" % stats.avg()) + " & " + str("%.2f" % stats.q95())+ " & " + str("%.2f" % stats.max)
                    tmp += "\\\\ [1ex]\n"
                    
            stats = self.generalCpuStatistics["total"]
            tmp += "\\textbf{" + "total" + "} & " + str("%.2f" % stats.min) + " & " + str("%.2f" % stats.avg()) + " & " + str("%.2f" % stats.q95())+ " & " + str("%.2f" % stats.max)
            tmp += "\\\\ [1ex]\n"
            tmp += "\
\\hline \n \
\\end{tabular} \n \
\\label{table:nonlin} \n \
\\end{table}\n\n"
            return tmp
    
        return ""
    
    def generateGeneralSection(self):

	#tmp = "\\graphicspath{.:epsfiles:" + os.getcwd() + "}\n\n"   
        tmp = "\\section{General system overview}\n\n"
        tmp += "\\subsection{CPU statistics}\n"
        tmp += "The figures presented below show the CPU utilization statistics for the whole system. \
        First one gives overview about the total usage while the second one provides details about the user, \
        system and iowait.\n"
        
        for fig in self.generalCpuSectionFigures:
            tmp += self.generateGeneralSectionFigure(fig,"height=250pt,")        
        
        #tmp += self.generateCpuStatisticsTable()            
        
        tmp += "\\clearpage\n"
        tmp += "\\pagebreak\n"
                
        tmp += "\\subsection{Memory statistics}\n"
        for fig in self.generalMemSectionFigures:
            tmp += self.generateGeneralSectionFigure(fig,"height=280pt,")        
        tmp += "\\pagebreak\n"
        
        tmp += "\\subsection{I/O statistics}\n"
        tmp += "\\pagebreak\n"
                
        tmp += "\\subsection{General per process statistics}\n"
        for fig in self.generalSectionFigures:
            tmp += self.generateGeneralSectionFigure(fig)

        tmp += "\\clearpage\n"    
        tmp += "\\pagebreak\n\n"
        
        if self.ignoredExecutionStats != None:
            tmp += self.generateIgnoredSection()
                                    
        return tmp
       
    def generateBootProcessSection(self):
        tmp = "\\section{Boot process}\n"
        tmp += "\\subsection{Boot statistics}\n"
        for fig in self.bootchartFigures:
            tmp += self.generateGeneralSectionFigure(fig)        
        tmp += "\\pagebreak\n"
        return tmp
       
    def generateTexDoc(self):
        tmp = self.generateGeneralSection() 
        #tmp += self.generateBootProcessSection()       
        tmp += self.generateProcessDetailSection()
        return tmp;
       
    def writeTexDocToFile(self,filename):
        fh = open(filename, "w")
        fh.write(self.generateTexDoc())
        fh.close
    
    def addIgnoredExecutionStatistics(self, executionStats):
        self.ignoredExecutionStats = executionStats

    def addIgnoredMaxCpuStatistics(self, maxCpuStats):
        self.ignoredMaxCpuStats = maxCpuStats

    def addIgnoredMaxMemStatistics(self, maxMemStats):
        self.ignoredMaxMemStats = maxMemStats


class PdfLatexGenerator:
    '''
    Class responsible for generating the pdf report out of the main tex document template
    
    @author: Krystian Brachmanski   
    @requires: subprocess
    '''

    log = logging.getLogger('yapcg.perfparser.latexUtils')
    '''
    Logger used in this class
    '''
    
    def generatePdf(self,document,workDir=None):
        '''
        This method is responsible for generating the pdf out of the latex tex file
        @param document: The main template name .tex
        @return: the name of the generated file or None if error occurs
        '''
        self.log.info("Generating pdf using pdflatex from " + document + ". Please wait...")
        OUT = open('pdflatex.out', 'w')
        try:
            subprocess.check_call(['pdflatex', '-interaction=batchmode','-shell-escape', '-halt-on-error','--synctex=1', document ],shell=False,stdout=OUT, stderr=OUT, cwd=workDir)
        except subprocess.CalledProcessError:
            self.log.error("pdflatex returned with non zero exit code check log files for errors: " + OUT.name)
            return None
        outReportName = os.path.splitext(document)[0] + ".pdf"
        self.log.info("Done. Output file : " + outReportName)
        return outReportName

class HtmlLatexGenerator:
    '''
    Class responsible for generating the html report out of the main tex document template
    
    @author: Krystian Brachmanski   
    @requires: subprocess
    '''

    log = logging.getLogger('yapcg.perfparser.latexUtils')
    '''
    Logger used in this class
    '''

    def generateHtml(self,document,workDir=None):
        '''
        This method is responsible for generating html pdf out of the latex tex file
        @param document: The main template name .tex
        @return: the name of the generated file or None if error occurs
        '''
        self.log.info("Generating html using latex2html from " + document + ". Please wait...")
        OUT = open('htmllatex.out', 'w')
        try:
            subprocess.check_call(['latex2html', '--dir=html', '--mkdir', document ],shell=False,stdout=OUT, stderr=OUT, cwd=workDir)
        except subprocess.CalledProcessError:
            self.log.error("latex2html returned with non zero exit code check log files for errors: " + OUT.name)
            return None
        outReportName = os.path.splitext(document)[0] + ".html"
        self.log.info("Done. Output file : " + outReportName)
        return outReportName
